#ifndef MATRIX_H
#define MATRIX_H

#include "bga/math/Quaternion.h"


/**
 * \namespace bga
 * \brief API namespace
 */
namespace bga
{

/**
 * \class Matrix
 * \brief defines a matrix
 */
template< unsigned int ROWS, unsigned int COLUMNS, typename T >
class Matrix
{
    protected:
        T array[ROWS*COLUMNS];

    public:
        /**
         * Constructor
         */
        Matrix();

        /**
         * Copy Constructor
         */
        Matrix(const Matrix<ROWS, COLUMNS, T>& matrix);

        /**
         * Destructor
         */
        virtual ~Matrix();

        /// IMPLEMENTS THE = OPERATOR AND PUT IT IN THE DERIVED CLASSES

        Matrix<ROWS, COLUMNS, T>& operator=(const Matrix<ROWS, COLUMNS, T>& matrix);


        /**
         * \brief get the array of value
         */
        const T* getArray() const;

        /**
         * \brief get the array of value
         */
        T* getArray();

        inline unsigned int fromIndices(unsigned int row, unsigned int col) const;


        /**
         * Indexed acces to the lements of the matrix
         */
        T& operator()(unsigned int row, unsigned int col);
        const T& operator()(unsigned int row, unsigned int col) const;

        /**
         * \brief multiplicate the current matrix with an other one whose the number of lines and columns
         *        are reverted
         * \param matrix : the other matrix
         * \return the resulting matrix
         */
        /*template< unsigned int COLUMNS1 >
        Matrix<ROWS, COLUMNS1, T> multiplicateMatrix(Matrix<COLUMNS, COLUMNS1, T> matrix);*/

        template< unsigned int COLUMNS1 >
        Matrix<ROWS, COLUMNS1, T> operator*(Matrix<COLUMNS, COLUMNS1, T> matrix);

        const std::string toString() const;
};


/**
 * \class Matrix
 * \brief defines a square matrix
 */
template< int N, typename T >
class SquaredMatrix : public Matrix<N, N, T>
{
    public:
        /**
         * \brief Allows to multiply two matrix
         * \param f : first matrix
         * \param g : second matrix
         * \param res : resulted matrix
         */
        static void multiplicateMatrix(T* f, T* g, T* res){};

        /**
         * \brief Draw a matrix on the console
         * \param m : matrix to draw
         */
        void drawMatrix(T* m){};

        /**
         * \brief convert a matrix into a quaternion
         * \param mat : matrix to convert
         */
        static Quaternion matrixToQuaternion(const T* mat){return Quaternion();};
};

template< typename T >
class SquaredMatrix<3, T> : public Matrix<3, 3, T>
{
    public:
        /**
         * \brief Allows to multiply two matrix
         * \param f : first matrix
         * \param g : second matrix
         * \param res : resulted matrix
         */
        static void multiplicateMatrix(const T* f, const T* g, T* res){/** Not yet implemented **/};

        /**
         * \brief Draw a matrix on the console
         * \param m : matrix to draw
         */
        static void drawMatrix(T* m){/** Not yet implemented **/};

        /**
         * \brief convert a matrix into a quaternion
         * \param mat : matrix to convert
         */
        static Quaternion matrixToQuaternion(const T* mat);
};

typedef SquaredMatrix<3, float> Matrix3f;
typedef SquaredMatrix<3, double> Matrix3d;
typedef SquaredMatrix<3, int> Matrix3i;

template< typename T >
class SquaredMatrix<4, T> : public Matrix<4, 4, T>
{
    public:

        SquaredMatrix<4, T>& operator=(const Matrix<4, 4, T>& matrix);

        /**
         * \brief Allows to multiply two matrix
         * \param f : first matrix
         * \param g : second matrix
         * \param res : resulted matrix
         */
        static void multiplicateMatrix(const T* f, const T* g, T* res);

        /**
         * \brief Draw a matrix on the console
         * \param m : matrix to draw
         */
        static void drawMatrix(T* m);

        /**
         * \brief convert a matrix into a quaternion
         * \param mat : matrix to convert
         */
        static Quaternion matrixToQuaternion(const T* mat){return Quaternion();};
};

typedef SquaredMatrix<4, float> Matrix4f;
typedef SquaredMatrix<4, double> Matrix4d;
typedef SquaredMatrix<4, int> Matrix4i;

}

#include "bga/math/Matrix.inl"


#endif

